////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxstatsprocessingnode.cpp
/// @brief Stats processing node class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxatomic.h"
#include "camxhwdefs.h"
#include "camxpipeline.h"
#include "camxpropertyblob.h"
#include "camxstatscommon.h"
#include "camxstatsprocessingnode.h"
#include "camxtrace.h"
#include "camxvendortags.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::StatsProcessingNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsProcessingNode::StatsProcessingNode()
{
    CAMX_LOG_INFO(CamxLogGroupStats, "StatsProcessingNodeStatsProcessingNode()");

    m_pNodeName    = "Stats";
    m_derivedNodeHandlesMetaDone = TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::~StatsProcessingNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsProcessingNode::~StatsProcessingNode()
{
    if (NULL != m_pStatsProcessorManager)
    {
        // Destroy the job dispatcher.
        m_pStatsProcessorManager->Destroy();
        m_pStatsProcessorManager = NULL;
    }

    if (NULL != m_pMultiStatsOperator)
    {
        CAMX_DELETE m_pMultiStatsOperator;
        m_pMultiStatsOperator = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsProcessingNode* StatsProcessingNode::Create(
    const NodeCreateInputData*  pCreateInputData,
    NodeCreateOutputData*       pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateOutputData);

    CamxResult               result              = CamxResultSuccess;
    UINT32                   propertyCount       = pCreateInputData->pNodeInfo->nodePropertyCount;
    StatsProcessingNode*     pStatsNode          = NULL;
    StatsNodeCreateData      createData          = {0};
    StatsInitializeCallback* pInitializecallback = NULL;

    pStatsNode = CAMX_NEW StatsProcessingNode();
    if (NULL != pStatsNode)
    {
        createData.pNode = pStatsNode;
        createData.instanceId = pCreateInputData->pNodeInfo->instanceId;
        createData.pStatsNotifier = static_cast<IStatsNotifier*>(pStatsNode);

        pStatsNode->m_skipPattern = 1;

        for (UINT32 count = 0; count < propertyCount; count++)
        {
            if (NodePropertyStatsSkipPattern == pCreateInputData->pNodeInfo->pNodeProperties[count].id)
            {
                pStatsNode->m_skipPattern = *static_cast<UINT*>(pCreateInputData->pNodeInfo->pNodeProperties[count].pValue);
            }
        }
        // Create stats job dispatcher.
        result = StatsProcessorManager::Create(&createData, &pStatsNode->m_pStatsProcessorManager);
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupStats, "StatsProcessorManager::Create failed = %d", result);
            CAMX_DELETE pStatsNode;

            pStatsNode = NULL;
        }
        else
        {
            pInitializecallback = &pStatsNode->m_statsInitializeData.initializecallback;
            pInitializecallback->pAECCallback = pCreateInputData->pAECAlgoCallbacks;
            pInitializecallback->pAWBCallback = pCreateInputData->pAWBAlgoCallbacks;
            pInitializecallback->pASDCallback = pCreateInputData->pASDAlgoCallbacks;
            pInitializecallback->pAFDCallback = pCreateInputData->pAFDAlgoCallbacks;
        }

    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupStats, "StatsProcessingNode::Create failed - out of memory");
        result = CamxResultENoMemory;
    }

    CAMX_LOG_INFO(CamxLogGroupStats, "StatsProcessingNode::Create result = %d", result);

    return pStatsNode;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::Destroy
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID StatsProcessingNode::Destroy()
{
    CAMX_DELETE this;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::ProcessingNodeInitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::ProcessingNodeInitialize(
    const NodeCreateInputData*  pCreateInputData,
    NodeCreateOutputData*       pCreateOutputData)
{
    CAMX_ENTRYEXIT_SCOPE(CamxLogGroupStats, SCOPEEventStatsProcessingNodeProcessingNodeInitialize);
    CamxResult result = CamxResultSuccess;

    m_pChiContext                   = pCreateInputData->pChiContext;
    pCreateOutputData->pNodeName    = m_pNodeName;

    m_bufferOffset = GetStaticSettings()->maxPipelineDelay;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::ProcessingNodeFinalizeInitialization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::ProcessingNodeFinalizeInitialization(
    FinalizeInitializationData* pFinalizeInitializationData)
{
    CamxResult result = CamxResultSuccess;

    // Store the required input information.
    m_statsInitializeData.pDebugDataPool = pFinalizeInitializationData->pDebugDataPool;
    m_statsInitializeData.pThreadManager = pFinalizeInitializationData->pThreadManager;
    m_statsInitializeData.pHwContext     = pFinalizeInitializationData->pHwContext;

    result = InitializePreviousSessionData();

    UINT inputPortId[StatsInputPortMaxCount];
    UINT numInputPort = 0;

    GetAllInputPortIds(&numInputPort, &inputPortId[0]);

    for (UINT inputIndex = 0; inputIndex < numInputPort; inputIndex++)
    {
        // need request - 4 buffer
        SetInputPortBufferDelta(inputIndex, m_bufferOffset);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::GetPropertyDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::GetPropertyDependencies(
    ExecuteProcessRequestData*  pExecuteProcessRequestData,
    StatsProcessRequestData*    pStatsProcessRequestDataInfo
    ) const
{
    CAMX_ENTRYEXIT(CamxLogGroupStats);
    CAMX_ASSERT_MESSAGE(NULL != pExecuteProcessRequestData, "pExecuteProcessRequestData NULL pointer");
    CAMX_ASSERT_MESSAGE(NULL != pStatsProcessRequestDataInfo, "pStatsProcessRequestDataInfo NULL pointer");

    CamxResult              result = CamxResultSuccess;
    NodeProcessRequestData* pNodeRequestData = pExecuteProcessRequestData->pNodeProcessRequestData;
    StatsDependency         statsDependency = { 0 };

    CAMX_ASSERT_MESSAGE(NULL != pNodeRequestData, "Node capture request data NULL pointer");
    CAMX_ASSERT_MESSAGE(NULL != m_pStatsProcessorManager, "stats processor manager NULL pointer");

    pStatsProcessRequestDataInfo->pDependencyUnit = &pNodeRequestData->dependencyInfo[0];

    // Check property dependencies
    result = m_pStatsProcessorManager->GetDependencies(pStatsProcessRequestDataInfo, &statsDependency);

    if (CamxResultSuccess == result)
    {
        if (statsDependency.propertyCount > 0)
        {
            // Update dependency request data for topology to consume
            pNodeRequestData->dependencyInfo[0].dependencyFlags.hasPropertyDependency = TRUE;
            pNodeRequestData->dependencyInfo[0].processSequenceId = 1;

            pNodeRequestData->numDependencyLists = 1;

            // Add property dependencies
            pNodeRequestData->dependencyInfo[0].propertyDependency.count = statsDependency.propertyCount;
            for (INT32 i = 0; i < statsDependency.propertyCount; i++)
            {
                pNodeRequestData->dependencyInfo[0].propertyDependency.properties[i] =
                    statsDependency.properties[i].property;
                pNodeRequestData->dependencyInfo[0].propertyDependency.offsets[i] =
                    statsDependency.properties[i].offset;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::GetBufferDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::GetBufferDependencies(
    ExecuteProcessRequestData*  pExecuteProcessRequestData
    ) const
{
    CAMX_ENTRYEXIT(CamxLogGroupStats);
    CAMX_ASSERT_MESSAGE(NULL != pExecuteProcessRequestData, "Execute Process Request Data NULL pointer");

    PerRequestActivePorts*  pEnabledPorts       = pExecuteProcessRequestData->pEnabledPortsInfo;
    NodeProcessRequestData* pNodeRequestData    = pExecuteProcessRequestData->pNodeProcessRequestData;
    CamxResult              result              = CamxResultSuccess;

    CAMX_ASSERT_MESSAGE(NULL != pNodeRequestData, "Node capture request data NULL pointer");
    CAMX_ASSERT_MESSAGE(NULL != pEnabledPorts, "Per Request Active Ports NULL pointer");

    PropertyDependency* pDeps = &pNodeRequestData->dependencyInfo[0].propertyDependency;

    for (UINT portIndex = 0; portIndex < pEnabledPorts->numInputPorts; portIndex++)
    {
        switch (portIndex)
        {
            case StatsInputPortAWBBG:
                pDeps->properties[pDeps->count++] = PropertyIDParsedAWBBGStatsOutput;
                break;
            case StatsInputPortBF:
                pDeps->properties[pDeps->count++] = PropertyIDParsedBFStatsOutput;
                break;
            case StatsInputPortBHist:
                pDeps->properties[pDeps->count++] = PropertyIDParsedBHistStatsOutput;
                break;
            case StatsInputPortHDRBE:
                pDeps->properties[pDeps->count++] = PropertyIDParsedHDRBEStatsOutput;
                break;
            case StatsInputPortHDRBHist:
                pDeps->properties[pDeps->count++] = PropertyIDParsedHDRBHistStatsOutput;
                break;
            case StatsInputPortIHist:
                pDeps->properties[pDeps->count++] = PropertyIDParsedIHistStatsOutput;
                break;
            case StatsInputPortCS:
                pDeps->properties[pDeps->count++] = PropertyIDParsedCSStatsOutput;
                break;
            case StatsInputPortRS:
                pDeps->properties[pDeps->count++] = PropertyIDParsedRSStatsOutput;
                break;
            case StatsInputPortTintlessBG:
                pDeps->properties[pDeps->count++] = PropertyIDParsedTintlessBGStatsOutput;
                break;
            default:
                CAMX_LOG_ERROR(CamxLogGroupStats, "Need to add new stats support");
                break;
        }
    }

    if (pDeps->count > 0)
    {
        pNodeRequestData->dependencyInfo[0].processSequenceId = 1;
        pNodeRequestData->dependencyInfo[0].dependencyFlags.hasPropertyDependency = TRUE;
        pNodeRequestData->numDependencyLists                                      = 1;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::GetMultiStatsDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::GetMultiStatsDependencies(
    ExecuteProcessRequestData*  pExecuteProcessRequestData
    ) const
{
    CAMX_ENTRYEXIT(CamxLogGroupStats);
    CAMX_ASSERT_MESSAGE(NULL != pExecuteProcessRequestData, "Execute Process Request Data NULL pointer");

    CamxResult              result              = CamxResultSuccess;
    BOOL                    isCrossSatisfied    = FALSE;
    NodeProcessRequestData* pNodeRequestData    = pExecuteProcessRequestData->pNodeProcessRequestData;
    DependencyUnit*         pDependencyUnit     = &(pNodeRequestData->dependencyInfo[0]);

    if ((TRUE == GetStaticSettings()->multiCameraEnable) &&
        (MultiCamera3ASyncDisabled != GetStaticSettings()->multiCamera3ASync))
    {
        if (NULL != m_pMultiStatsOperator)
        {
            result = m_pMultiStatsOperator->UpdateStatsDependencies(
                pDependencyUnit,
                pNodeRequestData->pCaptureRequest->requestId,
                pNodeRequestData->pCaptureRequest->pMultiRequestData,
                &isCrossSatisfied);
            CAMX_LOG_VERBOSE(CamxLogGroupStats, "After Update dependency: isCrossSatisfied:%d", isCrossSatisfied);

            if (CamxResultSuccess == result && TRUE == pDependencyUnit->dependencyFlags.hasPropertyDependency)
            {
                if (0 == pDependencyUnit->processSequenceId)
                {
                    pDependencyUnit->processSequenceId = 1;
                }
                if (0 == pNodeRequestData->numDependencyLists)
                {
                    pNodeRequestData->numDependencyLists = 1;
                }
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::PrepareStatsProcessRequestData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::PrepareStatsProcessRequestData(
    ExecuteProcessRequestData*  pExecuteProcessRequestData,
    StatsProcessRequestData*    pStatsProcessRequestData)
{
    CAMX_ENTRYEXIT(CamxLogGroupStats);

    CamxResult              result              = CamxResultSuccess;
    NodeProcessRequestData* pNodeRequestData    = pExecuteProcessRequestData->pNodeProcessRequestData;
    PerRequestActivePorts*  pEnabledPorts       = pExecuteProcessRequestData->pEnabledPortsInfo;

    CAMX_ASSERT_MESSAGE(NULL != pNodeRequestData, "Node request data NULL pointer");
    CAMX_ASSERT_MESSAGE(NULL != pEnabledPorts, "Per Request Active Ports NULL pointer");
    CAMX_ASSERT_MESSAGE(NULL != pNodeRequestData->pCaptureRequest, "Node capture request data NULL pointer");

    pStatsProcessRequestData->skipProcessing    = CanSkipAlgoProcessing(pNodeRequestData->pCaptureRequest->requestId);
    pStatsProcessRequestData->requestId         = pNodeRequestData->pCaptureRequest->requestId;
    pStatsProcessRequestData->pMultiRequestSync = pNodeRequestData->pCaptureRequest->pMultiRequestData;
    pStatsProcessRequestData->pNode             = this;
    pStatsProcessRequestData->algoAction        = StatsAlgoProcessRequest;

    if (FALSE == pStatsProcessRequestData->skipProcessing)
    {
        for (UINT i = 0; i < pEnabledPorts->numInputPorts; i++)
        {
            PerRequestInputPortInfo* pPerRequestInputPort = &pEnabledPorts->pInputPorts[i];

            CAMX_ASSERT_MESSAGE(NULL != pPerRequestInputPort, "Per Request Input PortNULL pointer");

            pStatsProcessRequestData->bufferInfo[i].statsType = StatsUtil::GetStatsType(pPerRequestInputPort->portId);
            // Removed fence and image buffer as they do not apply when coming from StatsParse and should not be used
        }

        pStatsProcessRequestData->bufferCount = pEnabledPorts->numInputPorts;

        if (NULL != pExecuteProcessRequestData->pTuningModeData)
        {
            pStatsProcessRequestData->pTuningModeData = pExecuteProcessRequestData->pTuningModeData;
        }
    }

    if ((TRUE == GetStaticSettings()->multiCameraEnable) &&
        (MultiCamera3ASyncDisabled != GetStaticSettings()->multiCamera3ASync))
    {
        UINT        tag[2]              = { 0 };
        VOID*       pData[2]            = { 0 };
        UINT        length              = CAMX_ARRAY_SIZE(tag);
        UINT64      configDataOffset[2] = { 0 };

        // Query information sent by multi camera controller
        VendorTagManager::QueryVendorTagLocation("com.qti.chi.multicamerainfo", "MultiCameraIdRole", &tag[0]);
        tag[0] |= InputMetadataSectionMask;
        VendorTagManager::QueryVendorTagLocation("com.qti.chi.multicamerainfo", "MasterCamera", &tag[1]);
        tag[1] |= InputMetadataSectionMask;
        GetDataList(tag, pData, configDataOffset, length);

        if (NULL != pData[0] && NULL != pData[1])
        {
            MultiCameraIdRole* pMCCInfo  = reinterpret_cast<MultiCameraIdRole*>(pData[0]);
            BOOL*              pIsMaster = reinterpret_cast<BOOL*>(pData[1]);

            pStatsProcessRequestData->cameraInfo.cameraId = GetPipeline()->GetCameraId();
            switch (pMCCInfo->currentCameraRole)
            {
                case CameraRoleTypeWide:
                    pStatsProcessRequestData->cameraInfo.cameraType = StatsCameraTypeWide;
                    break;
                case CameraRoleTypeTele:
                    pStatsProcessRequestData->cameraInfo.cameraType = StatsCameraTypeTele;
                    break;
                case CameraRoleTypeVR:
                    pStatsProcessRequestData->cameraInfo.cameraType = StatsCameraTypeVR;
                    break;
                case CameraRoleTypeDefault:
                    pStatsProcessRequestData->cameraInfo.cameraType = StatsCameraTypeDefault;
                    break;
                default:
                    CAMX_LOG_ERROR(CamxLogGroupStats, "Invalid camera type for request %llu",
                                   pStatsProcessRequestData->requestId);
                    pStatsProcessRequestData->cameraInfo.cameraType = StatsCameraTypeDefault;
                    break;
            }

            if (StatsCameraTypeDefault == pStatsProcessRequestData->cameraInfo.cameraType)
            {
                pStatsProcessRequestData->cameraInfo.algoRole = StatsAlgoRoleDefault;
            }
            else
            {
                pStatsProcessRequestData->cameraInfo.algoRole =
                    (*pIsMaster) ? StatsAlgoRoleMaster : StatsAlgoRoleSlave;
            }
            CAMX_LOG_VERBOSE(CamxLogGroupStats, "StatsNode CameraInfo ReqId:%llu CamId:%d Role:%-7s Type:%-7s",
                pStatsProcessRequestData->requestId,
                pStatsProcessRequestData->cameraInfo.cameraId,
                GetRoleName(pStatsProcessRequestData->cameraInfo.algoRole),
                GetCameraTypeName(pStatsProcessRequestData->cameraInfo.cameraType));
            m_cameraInfo = pStatsProcessRequestData->cameraInfo;
        }
        else
        {
            CAMX_LOG_INFO(CamxLogGroupStats, "Multi camera metadata not published for request %llu",
                          pStatsProcessRequestData->requestId);
            pStatsProcessRequestData->cameraInfo = m_cameraInfo;
        }
    }
    else
    {
        pStatsProcessRequestData->cameraInfo.algoRole   = StatsAlgoRoleDefault;
        pStatsProcessRequestData->cameraInfo.cameraId   = GetPipeline()->GetCameraId();
        pStatsProcessRequestData->cameraInfo.cameraType = StatsCameraTypeDefault;
    }


    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::ExecuteProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::ExecuteProcessRequest(
    ExecuteProcessRequestData* pExecuteProcessRequestData)
{
    CAMX_ENTRYEXIT_SCOPE_ID(CamxLogGroupStats, SCOPEEventStatsProcessingNodeExecuteProcessRequest,
        pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId);

    CAMX_LOG_VERBOSE(CamxLogGroupStats,
                     "StatsProcessingNode execute request for id %llu seqId %d",
                     pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId,
                     pExecuteProcessRequestData->pNodeProcessRequestData->processSequenceId);

    CamxResult              result                          = CamxResultSuccess;
    StatsProcessRequestData statsProcessRequestData         = {0};
    NodeProcessRequestData* pNodeRequestData                = pExecuteProcessRequestData->pNodeProcessRequestData;

    CAMX_ASSERT(pNodeRequestData->dependencyInfo[0].dependencyFlags.dependencyFlagsMask == 0);

    PrepareStatsProcessRequestData(pExecuteProcessRequestData, &statsProcessRequestData);

    CAMX_LOG_VERBOSE(CamxLogGroupStats, "Stats Node execute request for req %llu pipeline %d seqId %d",
        pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId,
        GetPipeline()->GetPipelineId(),
        pExecuteProcessRequestData->pNodeProcessRequestData->processSequenceId);

    if (0 == pExecuteProcessRequestData->pNodeProcessRequestData->processSequenceId)
    {
        // Initialize number of dependency lists to 0
        pExecuteProcessRequestData->pNodeProcessRequestData->numDependencyLists = 0;

        result = GetPropertyDependencies(pExecuteProcessRequestData, &statsProcessRequestData);

        if ((CamxResultSuccess == result) && (FALSE == statsProcessRequestData.skipProcessing))
        {
            result = GetBufferDependencies(pExecuteProcessRequestData);
        }

        if (CamxResultSuccess == result)
        {
            result = GetMultiStatsDependencies(pExecuteProcessRequestData);
        }
    }

    if (FALSE == Node::HasAnyDependency(pExecuteProcessRequestData->pNodeProcessRequestData->dependencyInfo))
    {
        if ((TRUE == GetStaticSettings()->multiCameraEnable) &&
            (MultiCamera3ASyncDisabled != GetStaticSettings()->multiCamera3ASync) &&
            (NULL != m_pMultiStatsOperator))
        {
            statsProcessRequestData.algoAction = m_pMultiStatsOperator->GetStatsAlgoAction();
        }

        // Execute process Request of the job dispatcher
        result = m_pStatsProcessorManager->OnProcessRequest(&statsProcessRequestData);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::PostPipelineCreate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::PostPipelineCreate()
{
    CamxResult              result              = CamxResultSuccess;
    CHIALGORITHMINTERFACE   chiAlgoInterface    = { 0 };
    INT32                   cameraID            = GetPipeline()->GetCameraId();

    // Store the required input information.
    m_statsInitializeData.pNode              = this;
    m_statsInitializeData.pStaticPool        = m_pChiContext->GetStaticMetadataPool(GetPipeline()->GetCameraId());
    m_statsInitializeData.pDebugDataPool     = GetPerFramePool(PoolType::PerFrameDebugData);
    m_statsInitializeData.pThreadManager     = GetThreadManager();
    m_statsInitializeData.pHwContext         = GetHwContext();
    m_statsInitializeData.pTuningDataManager = GetTuningDataManager();
    m_statsInitializeData.pPipeline          = GetPipeline();
    m_statsInitializeData.pNode              = this;

    // Remove the below hardcoding after testing
    m_statsInitializeData.statsStreamInitConfig.operationMode = StatsOperationModeNormal;
    StatsUtil::GetStatsStreamInitConfig(m_statsInitializeData.pPipeline->GetPerFramePool(PoolType::PerUsecase),
        &m_statsInitializeData.statsStreamInitConfig);

    // Get initial camera information
    if ((TRUE == GetStaticSettings()->multiCameraEnable) &&
        (MultiCamera3ASyncDisabled != GetStaticSettings()->multiCamera3ASync))
    {
        ChiCameraInfo cameraInfo;
        cameraInfo.pLegacy = static_cast<VOID*>(CAMX_CALLOC(sizeof(CameraInfo)));
        m_pChiContext->GetCameraInfo(cameraID, &cameraInfo);
        if (NULL != cameraInfo.pLegacy)
        {
            CAMX_FREE(cameraInfo.pLegacy);
            cameraInfo.pLegacy = NULL;
        }

        m_statsInitializeData.cameraInfo.cameraId = static_cast<UINT32>(cameraID);
        if (REAR == cameraInfo.sensorCaps.positionType)
        {
            m_statsInitializeData.cameraInfo.cameraType = StatsCameraTypeWide;
            m_statsInitializeData.cameraInfo.algoRole   = StatsAlgoRoleMaster;
        }
        else if (REAR_AUX == cameraInfo.sensorCaps.positionType)
        {
            m_statsInitializeData.cameraInfo.cameraType = StatsCameraTypeTele;
            m_statsInitializeData.cameraInfo.algoRole   = StatsAlgoRoleSlave;
        }
        else
        {
            m_statsInitializeData.cameraInfo.cameraType = StatsCameraTypeDefault;
            m_statsInitializeData.cameraInfo.algoRole   = StatsAlgoRoleDefault;
        }
    }
    else
    {
        m_statsInitializeData.cameraInfo.cameraId   = static_cast<UINT32>(cameraID);
        m_statsInitializeData.cameraInfo.cameraType = StatsCameraTypeDefault;
        m_statsInitializeData.cameraInfo.algoRole   = StatsAlgoRoleDefault;
    }

    chiAlgoInterface.pGetVendorTagBase       = ChiStatsSession::GetVendorTagBase;
    chiAlgoInterface.pGetMetadata            = ChiStatsSession::FNGetMetadata;
    chiAlgoInterface.pSetMetaData            = ChiStatsSession::FNSetMetadata;
    chiAlgoInterface.pQueryVendorTagLocation = ChiStatsSession::QueryVendorTagLocation;
    chiAlgoInterface.size                    = sizeof(CHIALGORITHMINTERFACE);

    if ((NULL != m_statsInitializeData.initializecallback.pAECCallback) &&
        (NULL != m_statsInitializeData.initializecallback.pAECCallback->pfnSetAlgoInterface))
    {
        m_statsInitializeData.initializecallback.pAECCallback->pfnSetAlgoInterface(&chiAlgoInterface);
    }

    if ((NULL != m_statsInitializeData.initializecallback.pAWBCallback) &&
        (NULL != m_statsInitializeData.initializecallback.pAWBCallback->pfnSetAlgoInterface))
    {
        m_statsInitializeData.initializecallback.pAWBCallback->pfnSetAlgoInterface(&chiAlgoInterface);
    }

    result = InitializePreviousSessionData();

    if (CamxResultSuccess == result)
    {
        // Initialize the stats process manager.
        result = m_pStatsProcessorManager->Initialize(&m_statsInitializeData);
    }

    // Handle multi camera usecase
    if (CamxResultSuccess == result)
    {
        result = InitializeMultiStats();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::InitializePreviousSessionData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::InitializePreviousSessionData()
{
    CamxResult result = CamxResultSuccess;

    /// @todo  (CAMX-523): Get the previous session data.

    CAMX_LOG_WARN(CamxLogGroupStats, "StatsProcessingNode Camx FW to implement a mechanism to save previous session data");

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::NotifyJobProcessRequestDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::NotifyJobProcessRequestDone(
    UINT64 requestId)
{
    CamxResult  result      = CamxResultSuccess;

    ProcessMetadataDone(requestId);
    ProcessRequestIdDone(requestId);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::CanSkipAlgoProcessing
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL StatsProcessingNode::CanSkipAlgoProcessing(
    UINT64 requestId
    ) const
{
    UINT skipFactor = m_skipPattern;
    // Stats node will publish default initialized output for maxPipelineDelay number of frames.
    // We should start skipping 1 frame after that. This way we will have all the result output from previous frame
    // to publish for the first skipped frame
    skipFactor = (requestId <= (FirstValidRequestId + GetStaticSettings()->maxPipelineDelay)) ? 1 : skipFactor;

    BOOL skipRequired = ((requestId % skipFactor) == 0) ? FALSE : TRUE;

    return skipRequired;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::InitializeMultiStats
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult StatsProcessingNode::InitializeMultiStats()
{
    CamxResult            result                = CamxResultSuccess;
    const StaticSettings* pStaticSettings       = GetStaticSettings();
    BOOL                  isMultiCameraRunning  = FALSE;

    if (TRUE == m_statsInitializeData.pPipeline->IsDualCamera())
    {
        isMultiCameraRunning = TRUE;
    }

    if (TRUE == isMultiCameraRunning)
    {
        if (MultiCamera3ASyncQTI == pStaticSettings->multiCamera3ASync)
        {
            CAMX_LOG_INFO(CamxLogGroupStats, "3A sync scheme selected: QTI");
            m_pMultiStatsOperator = CAMX_NEW QTIMultiStatsOperator;
        }
        else if (MultiCamera3ASyncSingleton == pStaticSettings->multiCamera3ASync)
        {
            CAMX_LOG_INFO(CamxLogGroupStats, "3A sync scheme selected: Singleton Algo");
            m_pMultiStatsOperator = CAMX_NEW SingletonStatsOperator;
        }
        else if (MultiCamera3ASyncDisabled == pStaticSettings->multiCamera3ASync)
        {
            CAMX_LOG_INFO(CamxLogGroupStats, "3A sync scheme selected: No 3A sync needed");
            m_pMultiStatsOperator = CAMX_NEW NoSyncStatsOperator;
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupStats, "3A sync scheme selected: Invalid scheme");
            m_pMultiStatsOperator = CAMX_NEW NoSyncStatsOperator;
        }

        if (NULL != m_pMultiStatsOperator)
        {
            MultiStatsData multiStatsData;

            multiStatsData.algoSyncType     = StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB;
            multiStatsData.algoRole         = StatsAlgoRole::StatsAlgoRoleDefault;
            multiStatsData.pipelineId       = m_statsInitializeData.pPipeline->GetPipelineId();
            result = m_statsInitializeData.pPipeline->GetIntraRealtimePipelineId(multiStatsData.pipelineId,
                                                                                 &multiStatsData.peerPipelineId);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_INFO(CamxLogGroupStats, "Failed to get peer pipeline ID!");
                CAMX_DELETE m_pMultiStatsOperator;
                m_pMultiStatsOperator = NULL;
                result = CamxResultSuccess;
            }
            else
            {
                multiStatsData.pHwContext       = m_statsInitializeData.pHwContext;
                multiStatsData.pNode            = this;

                m_pMultiStatsOperator->Initialize(&multiStatsData);
            }
        }
        else
        {
            result = CamxResultENoMemory;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::GetRoleName()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const CHAR* StatsProcessingNode::GetRoleName(
    StatsAlgoRole role)
{
    switch(role)
    {
        case StatsAlgoRole::StatsAlgoRoleDefault:
            return "Default";
        case StatsAlgoRole::StatsAlgoRoleMaster:
            return "Master";
        case StatsAlgoRole::StatsAlgoRoleSlave:
            return "Slave";
        default:
            return "Unknown";

    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// StatsProcessingNode::GetCameraTypeName()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const CHAR* StatsProcessingNode::GetCameraTypeName(
    StatsCameraType type)
{
    switch(type)
    {
        case StatsCameraTypeDefault:
            return "Default";
        case StatsCameraTypeWide:
            return "Wide";
        case StatsCameraTypeTele:
            return "Tele";
        case StatsCameraTypeVR:
            return "VR";
        default:
            return "Unknown";

    }
}
CAMX_NAMESPACE_END
